#include "../Logs/LzcLog.h"
#include <chrono>

void PerfTest(const std::string &logger_name, size_t thread_num, size_t log_num, size_t log_size)
{
    // 1. 获取日志器
    LzcLog::Logger::ptr logger = LzcLog::GetLogger(logger_name);
    if (logger == nullptr)
        return;
    std::cout << "测试日志: " << log_num << "条，总大小: " << (log_num * log_size) / 1024 << "KB" << std::endl;
    // 2. 组织指定长度的日志消息
    std::string log(log_size - 1, 'Z'); // 后面还会加一个换行符
    // 3. 创建指定数量的线程
    std::vector<double> cost_carry(thread_num);
    std::vector<std::thread> threads;
    size_t per_thread_log_num = log_num / thread_num;
    for (int i = 0; i < thread_num; ++i)
    {
        threads.emplace_back([&, i]() {
        // 4. 线程内获取开始时间
        auto start = std::chrono::high_resolution_clock::now();
        // 5. 循环写日志
        for(int j = 0; j < per_thread_log_num; ++j)
          logger->LOG_FATAL("%s", log.data()); // 这里面会加一个换行符
        // 6. 线程内获取结束时间
        auto end = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> cost = end - start;
        cost_carry[i] = cost.count();
        std::cout << "\t线程" << i << ":\t" << "输出日志数量: " << per_thread_log_num << ", 耗时: " << cost.count() << "s" << std::endl; 
        });
    }
    for (int i = 0; i < thread_num; ++i)
        threads[i].join();
    // 7. 总耗时(不包括线程的创建和等待的时间)，在多线程中，是并发处理的，取最大的。
    double max_cost = cost_carry[0];
    for (int i = 1; i < thread_num; ++i)
        max_cost = std::max(max_cost, cost_carry[i]);
    // 8. 输出测试结果
    size_t per_sec_log_num = log_num / max_cost;               // 每秒输出的日志数量
    size_t per_sec_log_size = (per_sec_log_num * log_size) / 1024; // 每秒输出的日志大小 KB
    std::cout << "\t总耗时: " << max_cost << "s" << std::endl;
    std::cout << "\t每秒输出日志数量: " << per_sec_log_num << "条" << std::endl;
    std::cout << "\t每秒输出日志大小: " << per_sec_log_size << "KB" << std::endl;
}

void SyncLogger()
{
    std::unique_ptr<LzcLog::LoggerBuilder> builder = std::make_unique<LzcLog::GlobalLoggerBuilder>();
    builder->BuildLoggerType(LzcLog::LoggerType::LOGGER_SYNC); // 同步日志器
    builder->BuildLoggerName("sync_logger");
    builder->BuildLoggerFormatter("%m%n"); // %n就是换行符
    builder->BuildLoggerSink<LzcLog::FileSink>("./logfile/sync.log");
    builder->Build();
    std::cout << "单线程同步: " << std::endl;
    PerfTest("sync_logger", 1, 1e6, 100);
    std::cout << "4线程同步: " << std::endl;
    PerfTest("sync_logger", 4, 1e6, 100);
}

void AsyncLogger()
{
    std::unique_ptr<LzcLog::LoggerBuilder> builder = std::make_unique<LzcLog::GlobalLoggerBuilder>();
    builder->BuildLoggerType(LzcLog::LoggerType::LOGGER_ASYNC); // 异步日志器
    builder->BuildLoggerName("async_logger");
    builder->BuildLoggerFormatter("%m%n"); // %n就是换行符
    builder->BuildUnSafeAsync();           // 扩容，极限测试(将因为生产者缓冲区满了而等待的情况，排除在外)
    builder->BuildLoggerSink<LzcLog::FileSink>("./logfile/async.log");
    builder->Build();
    std::cout << "单线程异步: " << std::endl;
    PerfTest("async_logger", 1, 1e6, 100);
    std::cout << "4线程异步: " << std::endl;
    PerfTest("async_logger", 4, 1e6, 100);
}

// 多线程同步，因为有锁冲突，所以比单线程同步慢。
// 异步只要写到内存缓冲区就行，所以比同步快。
// 同步(写到磁盘)受限于磁盘的性能，异步(写到内存缓冲区)受限于CPU与内存的性能，
//   但是CPU和内存的上限很高，所以异步性能的提升更加明显。

int main()
{
    SyncLogger();
    AsyncLogger();
    return 0;
}